import {quote} from 'shell-quote';
import { Rule, RuleContent, Rules } from "./rule";
import Constants from './constant';

function shellQuoteWindows(command:string[]) {
    return `"${command[0].replace(/^\\$/, "\\\\").replace(/^"$/, "\\\"")}"`;
}

const osRules = Constants.osRules;
const debug = Constants.debug;

export default function replace(
    template: string,
    data: { [name: string]: string },
) {
    const result: string[] = [];
    let word: string[] = [];

    let inTemplateBlock = false;
    let lastCharDollar = false;

    function finishWord() {
        if (word.length==0) return;
        result.push(word.join(""));
        word = [];
    }

    for (let i = 0; i < template.length; i++) {
        const currentChar = template[i];

        if (inTemplateBlock) {
            // template end.
            if (currentChar == "}") {
                const name = word.join("");
                word = [];
                inTemplateBlock = false;

                const value: string = data[name] || "[unknown]";
                if (!data[name] && debug) console.log(`${name} not found!`);

                result.push((osRules.os.name=="windows" ? shellQuoteWindows : quote)([value]));
            } else {
                word.push(currentChar);
            }
            continue;
        }

        // template starts.
        if (currentChar == "{" && lastCharDollar) {
            lastCharDollar = false;
            inTemplateBlock = true;
            finishWord();
            continue;
        }

        // push the $ missing.
        if (lastCharDollar) {
            word.push("$");
        }

        lastCharDollar = false;

        // maybe { behind.
        if (currentChar == "$") {
            lastCharDollar = true;
            continue;
        }

        // normal char.
        word.push(currentChar);
    }

    finishWord();
    return result.join("");
}

function formatArguments(
    arguments_: any[],
    data: { [name: string]: string | any },
    ruleArguments: RuleContent,
): string {
    const result:string[] = [];

    arguments_.forEach((argument) => {
        let allow = true;
        let value:string;

        if (typeof argument != "string") {
            const rules = argument.rules.map((rule: any) => {
                return new Rule(rule);
            });

            if (!Rules.check(rules, ruleArguments)) {
                allow = false;
            }
            value = argument.value;
        } else {
            value = argument;
        }

        if (!allow) return;
        return result.push(replace(value, data));
    });

    return result.join(" ");
}

export {
    formatArguments,
    shellQuoteWindows
}